package com.niiwoo.civet.mobile.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.rpc.RpcContext;
import com.niiwoo.civet.mobile.util.ParameterValidator;
import com.niiwoo.civet.mobile.vo.request.OrganizationInfoReqVO;
import com.niiwoo.civet.mobile.vo.request.user.OrgUserLoginVO;
import com.niiwoo.civet.mobile.vo.response.OrgGetSMSCodeRespVO;
import com.niiwoo.civet.mobile.vo.response.OrganizationInfoRespVO;
import com.niiwoo.civet.mobile.vo.response.user.LoginResponseVO;
import com.niiwoo.civet.user.constants.LoginAccountErrorCode;
import com.niiwoo.civet.user.constants.OrgRedisConstants;
import com.niiwoo.civet.user.dto.request.OrgUserLoginReqDTO;
import com.niiwoo.civet.user.dto.request.OrganizationInfoReqDTO;
import com.niiwoo.civet.user.dto.response.OrganizationInfoRespDTO;
import com.niiwoo.civet.user.enums.OrganizationEnum;
import com.niiwoo.civet.user.service.OrganizationDubboService;
import com.niiwoo.civet.user.service.OrganizationInfoDubboService;
import com.niiwoo.tripod.base.component.PrivacyMasks;
import com.niiwoo.tripod.base.helper.StringHelper;
import com.niiwoo.tripod.consumer.component.FileUploadHandler;
import com.niiwoo.tripod.consumer.helper.Exceptions;
import com.niiwoo.tripod.consumer.util.FileMediaExtensions;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.web.annotation.AuthIgnore;
import com.niiwoo.tripod.web.properties.ShiroProperties;
import com.niiwoo.tripod.web.shiro.OrgUsernamePasswordToken;
import com.niiwoo.tripod.web.shiro.UserAuthPrincipal;
import com.niiwoo.tripod.web.util.PrincipalContext;
import com.niiwoo.tripod.web.vo.Empty;
import com.niiwoo.tripod.web.vo.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("organizationInfo")
@Api(tags = "企业投资人(李乐)")
@Slf4j
public class OrganizationInfoController {


    @Autowired
    private PrivacyMasks privacyMasks;
    @Autowired
    private ShiroProperties shiroProperties;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Reference(version = "1.0.0")
    OrganizationDubboService organizationDubboService;

    @Reference(version = "1.0.0")
    OrganizationInfoDubboService organizationInfoDubboService;

    @Autowired
    private FileUploadHandler fileUploadHandler;


    //盐值长度
    private static final int SALT_LENGTH = 5;

    @PostMapping("getSMSCode")
    @AuthIgnore
    @ApiOperation(value = "企业投资人登录-确认(李乐)", notes = "respCode(USR19123='请输入用户名' USR19124='请输入密码', USR19118='用户名不存在', USR19119='密码错误，请重新输入');    响应：smsToken")
    public Result<OrgGetSMSCodeRespVO> getSMSCode(@RequestBody @ApiParam(name = "传递用户,密码") OrgUserLoginVO userLoginVO) {
        if(userLoginVO.getUserName() == null) {
            Exceptions.throwBizException(LoginAccountErrorCode.USER_NAME_NOT_BLANK);
        }

        if(userLoginVO.getPassword() == null) {
            Exceptions.throwBizException(LoginAccountErrorCode.PASS_WORD_NOT_BLANK);
        }

        OrganizationInfoRespDTO organizationInfoRespDTO = organizationDubboService.selectOrganizationInfoByAgentUserName(userLoginVO.getUserName());
        if(organizationInfoRespDTO == null || !organizationInfoRespDTO.getType().equals(OrganizationEnum.INVESTTOR_ORG.getType())) {
            // 用户名不存在
            Exceptions.throwBizException(LoginAccountErrorCode.USERS_NAME_NOT_EXIST);
        }

        //数据由三方产生，密码先经过团贷加密然后经过shiro迭代三次加密
        if(! organizationInfoRespDTO.getPassword().equals(encryptionPassword(userLoginVO.getPassword(), organizationInfoRespDTO.getSalt()))) {
            // 密码错误
            Exceptions.throwBizException(LoginAccountErrorCode.ORG_PASSWORD_ERROR);
        }

        String smsToken = UUID.randomUUID().toString();
        boundHashOps().put(userLoginVO.getUserName(), smsToken);
        return Result.with(new OrgGetSMSCodeRespVO(smsToken, null));
    }

    private BoundHashOperations<String, String, Object> boundHashOps() {
        BoundHashOperations<String, String, Object> boundHashOps = redisTemplate.boundHashOps("orgGetSMSCodeToken");
        boundHashOps.expire(30, TimeUnit.MINUTES);
        return boundHashOps;
    }

    @PostMapping("/sendSMSCode")
    @ApiOperation(value = "企业投资人登录-发送短信验证码(李乐)", notes = "respCode(USR19122:登录超时，请返回上一步重新登录, USR19118='用户名不存在'， USR19121:验证码发送失败, USR19121: 两次发送需要间隔60秒，请耐心等待)")
    @AuthIgnore
    public Empty sendSmsCode(@RequestBody @ApiParam(name = "传递用户名, smsToken") OrgUserLoginVO userLoginVO) {
        String smsToken = (String) boundHashOps().get(userLoginVO.getUserName());
        if(smsToken == null || !smsToken.equals(userLoginVO.getSmsToken())) {
            Exceptions.throwBizException(LoginAccountErrorCode.LOGIN_TIMEOUT);
        }

        OrganizationInfoRespDTO organizationInfoRespDTO = organizationDubboService.selectOrganizationInfoByAgentUserName(userLoginVO.getUserName());
        if(organizationInfoRespDTO == null) {
            Exceptions.throwBizException(LoginAccountErrorCode.USERS_NAME_NOT_EXIST);
        }

        String sdate = FastDateFormat.getInstance("yyyyMMdd").format(new Date());
        String nextKey = OrgRedisConstants.VERIFY_CODE_PREFIX_NEXT + organizationInfoRespDTO.getAgentPhoneNumber() + "-" + sdate;
        String serverCode = stringRedisTemplate.opsForValue().get(nextKey);
        if (StringUtils.isNotEmpty(serverCode)) {
            throw new BizException(LoginAccountErrorCode.SEND_SMS_CODE_NEED_WAIT);
        }
        boolean sendFlag = organizationDubboService.sendSmsByLogin(organizationInfoRespDTO.getAgentPhoneNumber());
        if (!sendFlag) {
            throw new BizException(LoginAccountErrorCode.SMS_SEND_FAIL);
        }
        return Empty.getInstance();
    }


    @PostMapping("/login")
    @ApiOperation(value = "登录(李乐)", notes = "respCode(USR19120='请输入正确的验证码')  data(1：第一次登入，2:管理员重置密码)")
    @AuthIgnore
    public Result<LoginResponseVO> login(@RequestBody @ApiParam(name = "传递用户,密码,smsToken， 验证码") OrgUserLoginVO userLoginVO) {
        if(userLoginVO.getUserName() == null) {
            Exceptions.throwBizException(LoginAccountErrorCode.USER_NAME_NOT_BLANK);
        }

        if(userLoginVO.getPassword() == null) {
            Exceptions.throwBizException(LoginAccountErrorCode.PASS_WORD_NOT_BLANK);
        }

        String smsToken = (String) boundHashOps().get(userLoginVO.getUserName());
        if(smsToken == null || !smsToken.equals(userLoginVO.getSmsToken())) {
            throw new BizException(LoginAccountErrorCode.SMS_CODE_NOT_MATCH);
        }

        OrganizationInfoRespDTO organizationInfoRespDTO = organizationDubboService.selectOrganizationInfoByAgentUserName(userLoginVO.getUserName());
        if(organizationInfoRespDTO == null || !organizationInfoRespDTO.getAgentUserName().equals(userLoginVO.getUserName())) {
            throw new BizException(LoginAccountErrorCode.LOGIN_TIMEOUT);
        }

        String sdate = FastDateFormat.getInstance("yyyyMMdd").format(new Date());
        String codeKey = OrgRedisConstants.verify_CODE_PREFIX + organizationInfoRespDTO.getAgentPhoneNumber() + "-" + sdate;
        String serverCode = stringRedisTemplate.opsForValue().get(codeKey);
        if(serverCode == null || !serverCode.equals(userLoginVO.getSmsCode())) {
            throw new BizException(LoginAccountErrorCode.SMS_CODE_NOT_MATCH);
        }

        //数据由三方产生，密码先经过团贷加密然后经过shiro迭代三次加密
        String password = ParameterValidator.toTuanDaiPassword(userLoginVO.getPassword());
        OrgUsernamePasswordToken usernamePasswordToken = new OrgUsernamePasswordToken(userLoginVO.getUserName(), password);

        Subject subject = SecurityUtils.getSubject();
        try {
            //验证登入
            subject.login(usernamePasswordToken);
        } catch (UnknownAccountException e) {
            insertLogin(userLoginVO, (byte) -1, "账号不存在");
            // 用户名不存在
            Exceptions.throwBizException(LoginAccountErrorCode.USERS_NAME_NOT_EXIST);
        } catch (AuthenticationException e) {
            insertLogin(userLoginVO, (byte) -1, "密码错误");
            Exceptions.throwBizException(LoginAccountErrorCode.ORG_PASSWORD_ERROR);
        }
        insertLogin(userLoginVO, (byte) 1, "登录成功");

        LoginResponseVO response = new LoginResponseVO();
        String newToken = SecurityUtils.getSubject().getSession().getId().toString();
        response.setToken(newToken);
        response.setUserId(PrincipalContext.getUserId());
//        response.setLoginMobileNo(request.getLoginMobileNo());
        response.setLastLoginTime(new Date());
        response.setNeedUpdatePassword(organizationInfoRespDTO.getNeedUpdatePassword());

        Result<OrganizationInfoRespVO> organizationInfo = baseInfo();
        response.setOrganizationInfo(organizationInfo.getData());
        return Result.with(response);

    }

    @PostMapping("/updatePassword")
    @ApiOperation(value = "修改密码(李乐)", notes = "respCode(USR19020:修改密码失败)")
    public Empty updatePassword(@RequestHeader(value = "token") String token, @RequestBody @ApiParam(name = "传递新密码, NeedUpdatePassword!=1/2时需要传原密码") OrgUserLoginVO userLoginVO) {
        UserAuthPrincipal userPrincipal = PrincipalContext.getUserPrincipal();
        OrganizationInfoRespDTO organizationInfoRespDTO = organizationInfoDubboService.selectById(userPrincipal.getUserId());
        if(organizationInfoRespDTO == null) {
            throw new RuntimeException("企业用户不存在");
        }

        boolean isNeedUpdatePassword = "1".equals(organizationInfoRespDTO.getNeedUpdatePassword()) || "2".equals(organizationInfoRespDTO.getNeedUpdatePassword());
        if(!isNeedUpdatePassword) {
            if(! organizationInfoRespDTO.getPassword().equals(encryptionPassword(userLoginVO.getPassword(), organizationInfoRespDTO.getSalt()))) {
                // 密码错误
                Exceptions.throwBizException(LoginAccountErrorCode.ORG_PASSWORD_ERROR);
            }
        }

        String salt = StringHelper.genRandomStr(SALT_LENGTH);
        String newPassword = encryptionPassword(userLoginVO.getNewPassword(), salt);
        OrganizationInfoReqDTO organizationInfoReqDTO = new OrganizationInfoReqDTO();
        organizationInfoReqDTO.setPassword(newPassword);
        organizationInfoReqDTO.setSalt(salt);
        organizationInfoReqDTO.setId(PrincipalContext.getUserId());
        int b = organizationDubboService.updatePassword(organizationInfoReqDTO);
        if (b == 0) {
            throw new BizException(LoginAccountErrorCode.RESET_LOGIN_PASSWORD_FAIL);
        }

        if(!isNeedUpdatePassword) {
            return logout(token);
        }
        return Empty.getInstance();
    }



    @GetMapping("/logout")
    @ApiOperation(value = "注销（李乐）")
    @AuthIgnore
    public Empty logout(@RequestHeader(value = "token") String token) {
//        SecurityUtils.getSubject().logout(); // 没法传递token, 需要到Token里增加sessionId字段, 然后Realm重写getAuthorizationCacheKey方法
        redisTemplate.delete(token);
        return Empty.getInstance();
    }


    /**
     * 加密密码，规则：先进行团贷密码加密，然后进行三次shiro三次密码加密
     *
     * @param password
     * @param salt
     * @return
     */
    private String encryptionPassword(String password, String salt) {
        ShiroProperties.HashedCredentialProperties hashedCredential = shiroProperties.getHashedCredential();
        return new SimpleHash(hashedCredential.getHashAlgorithName(), ParameterValidator.toTuanDaiPassword(password), salt, hashedCredential.getHashIterations()).toString();
    }


    /**
     * 进行用户登入日志记录
     *
     * @param userLoginVO 登入信息
     * @param status      状态
     * @param msg         信息
     */
    private void insertLogin(OrgUserLoginVO userLoginVO, Byte status, String msg) {
        OrgUserLoginReqDTO orgUserLoginReqDTO = new OrgUserLoginReqDTO();
        orgUserLoginReqDTO.setLoginTime(new Date());
        orgUserLoginReqDTO.setLoginIp(userLoginVO.getLoginIp());
        orgUserLoginReqDTO.setMsg(msg);
        orgUserLoginReqDTO.setStatus(status);
        orgUserLoginReqDTO.setUserName(userLoginVO.getUserName());
        RpcContext.getContext().asyncCall(() -> organizationDubboService.insertLoginRecord(orgUserLoginReqDTO));

    }


    @PostMapping("/baseInfo")
    @ApiOperation(value = "基本信息(李乐)")
    public Result<OrganizationInfoRespVO> baseInfo() {
        String userId = PrincipalContext.getUserId();
        OrganizationInfoRespDTO organizationInfoRespDTO = organizationInfoDubboService.selectById(userId);
        if(organizationInfoRespDTO == null) {
            throw new RuntimeException("企业用户不存在");
        }
        organizationInfoRespDTO.setPassword(null);
        organizationInfoRespDTO.setSalt(null);

        OrganizationInfoRespVO respVO = new OrganizationInfoRespVO();
        BeanUtils.copyProperties(organizationInfoRespDTO, respVO);

        respVO.setLegalPersonCertNumber(decryptPrivacy(respVO.getLegalPersonCertNumber()));
        respVO.setLegalPersonPhoneNumber(decryptPrivacy(respVO.getLegalPersonPhoneNumber()));
        respVO.setContactsPhoneName(decryptPrivacy(respVO.getContactsPhoneName()));

        return Result.with(respVO);
    }

    public String decryptPrivacy(String str) {
        try {
            if(str == null) {
                return null;
            }
            return privacyMasks.decryptPrivacy(str);
        }catch (Exception e) {
            log.error("解密失败", e);
            return str;
        }
    }

    @PostMapping("updateNickName")
    @ApiOperation(value = "修改昵称(李乐)")
    public Result<Integer> updateNickName(@RequestBody OrganizationInfoReqVO reqVO) {
        OrganizationInfoReqDTO reqDTO = new OrganizationInfoReqDTO();
        reqDTO.setId(PrincipalContext.getUserId());
        reqDTO.setNickName(reqVO.getNickName());
        int i = organizationInfoDubboService.updateNickName(reqDTO);
        return Result.with(i);
    }

    @PostMapping("updateHeadImageUrl")
    @ApiOperation(value = "修改为系统头像(李乐)")
    public Result<Integer> updateHeadImageUrl(@RequestBody OrganizationInfoReqVO reqVO) {
        OrganizationInfoReqDTO reqDTO = new OrganizationInfoReqDTO();
        reqDTO.setId(PrincipalContext.getUserId());
        reqDTO.setHeadImage(reqVO.getHeadImageUrl());
        int i = organizationInfoDubboService.updateHeadImage(reqDTO);
        return Result.with(i);
    }

    @PostMapping("updateHeadImage")
    @ApiOperation(value = "修改头像(李乐)")
    public Result<Integer> updateHeadImage(@RequestParam("headImage") MultipartFile headImage) throws IOException {
        byte[] imageBytes = headImage.getBytes();
        String extension = FileMediaExtensions.parseMediaExtension(imageBytes);
        String fileUrl = fileUploadHandler.upload(imageBytes, false, extension == null ? new String[] {"jpg"} : ArrayUtils.EMPTY_STRING_ARRAY).getImageUrl();

        OrganizationInfoReqDTO reqDTO = new OrganizationInfoReqDTO();
        reqDTO.setId(PrincipalContext.getUserId());
        reqDTO.setHeadImage(fileUrl);
        int i = organizationInfoDubboService.updateHeadImage(reqDTO);

        return Result.with(i);
    }

}
